■ アンセーフ、ポインタ、DLL                                                         

■ C#でポインタを使う方法


 ガーベジコレクタの管理対象メモリ領域に配置される変数よりも高速に変数を操作したい場合や呼び出したDLL関数がポインタを使用している場合等
ポインタを使いいた場合ががあります。
 以下に、ポインタを使った例を紹介します。 C#でポインタをつか場合はunsafe 修飾子をクラスや関数の宣言に追加する必要があります。 更にコンパイラのコンパイル条件にアンセーフコンパイルの許可を追加する必要があります。
アンセーフコンパイル: @プロジェクトのプリパティを開く Aビルドのタブをひらく Bアンセーフコードの許可にチェックを追加する。
                尚、コマンドラインコンパイルの場合は コンパイラオプションに /unsafe を追加する。
<実行結果>

    

          

   <プログラム例>


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pointer_Console   //アンマネージコード //ポインタをC#でつかう
{
    unsafe class Program    //unsafe追加
    {

        static void Test(char theChar)  //static 必須
        {
            char theChar2 = 'X';

            System.Console.WriteLine("theChar2 = {0}", theChar2);   
         
            char* pChar = &theChar; //この関数に渡された文字のアドレスを新規のポインタに代入   //ポインタpChar初期化
            theChar2 = *pChar;      //渡されたアドレスの値を theChar2にセット //theChar2 = *theChar;この直接置き換えはコンパイラNG
            System.Console.WriteLine("theChar2 = {0}", theChar2);

            void* pVoid = pChar;    //char型のポインタをvoid型に変換
            int* pInt = (int*)pVoid;    //void型のポインタをintのポインタでキャスト → pIntの値を整数化

            System.Console.WriteLine("Value of theChar = {0}", theChar);    //theCharの値
            System.Console.WriteLine("Address of theChar = {0:X4}", (int)pChar);    //theCharのアドレス
            System.Console.WriteLine("Value of pChar = {0}", *pChar);   //pCharの値   
            System.Console.WriteLine("Value of pInt = {0}", *pInt);     //pIntの値

            System.Console.WriteLine("");     //改行+1

        }
        
        static void Main(string[] args)
        {

            Test('A');
            Test('B');
            Test('C');


        }

    }
}





<上記コンソールのコピー>
theChar2 = X
theChar2 = A
Value of theChar = A
Address of theChar = 1FEED0
Value of pChar = A
Value of pInt = 65

theChar2 = X
theChar2 = B
Value of theChar = B
Address of theChar = 1FEED0
Value of pChar = B
Value of pInt = 66

theChar2 = X
theChar2 = C
Value of theChar = C
Address of theChar = 1FEED0
Value of pChar = C
Value of pInt = 67

続行するには何かキーを押してください . . .





 DLL関数の呼び出し方(基本)

[DllImport("*.dll")] private extern static 関数名(引数、引数、……)という書式から、プログラムで利用する関数と 関数をエクスポートする
DLLを呼び出します。
 WindowsのDLL(Win32 API)と.NET Frameworkとでは型の管理方法が違うため、型の相互変換(マーシャリング)が必要となります。 
パラメータや戻り値の型に関しては、以下のように対応するC#の型を指定します。
APIでの型名
(括弧内は対応するC言語の型)
対応するC#の型
(括弧内は.NET Frameworkでの型名)
HANDLE (void *) System.IntPtr
BYTE (unsigned char) byte (System.Byte)
SHORT (short) short (System.Int16)
WORD (unsigned short) ushort (System.UInt16)
INT (int) int (System.Int32)
LONG (long) int (System.Int32)
UINT (unsigned int) uint (System.UInt32)
DWORD, ULONG (unsigned long) uint (System.UInt32)
BOOL (long) bool (System.Boolean)
CHAR (char) char (System.Char)
LPSTR (char *) System.Text.StringBuilder
LPWSTR (wchar_t *) System.Text.StringBuilder
LPCSTR (const char *) string (System.String)
LPCWSTR (const wchar_t *) string (System.String)
FLOAT (float) float (System.Single)
DOUBLE (double) double (System.Double)

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Runtime.InteropServices; //DLL Import で追加 namespace Beep { public partial class Form1 : Form { [DllImport("kernel32.dll")] //関数をエクスポートしているDLLのファイル名を指定 private extern static bool Beep(uint dwFreq, uint dwDuration); //関数の実体が外部にあることを表すextern修飾子と、静的なメンバであることを表すstatic修飾子を必ず指定する。 public Form1() { InitializeComponent(); } //ドレミファソラシド音が発生する private void button1_Click(object sender, EventArgs e) { Beep(262, 500); // ド Beep(294, 500); // レ Beep(330, 500); // ミ Beep(349, 500); // ファ Beep(392, 500); // ソ Beep(440, 500); // ラ Beep(494, 500); // シ Beep(523, 500); // ド } } }

 


■ DLL関数に文字列を渡す

フォームのタイトルの文字列を渡した例です。                                                     <実行結果>
 


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices; // DLL Import


namespace DLL_Windowtext
{
    public partial class Form1 : Form
    {
        [DllImport("User32.Dll", EntryPoint = "SetWindowText")] // [DllImport("User32.Dll")] でも可
        public static extern void SetWindowText(int hwnd, string text);


        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int hwnd = (int)this.Handle;        //ハンドル取得
            string s = "YS電子工作ラボ";         //フォームのタイトル設定
            SetWindowText(hwnd, s);


        }
    }
}

 



<実行前>




<実行後>



■ DLL関数から文字列を取得する

DLL関数から文字列を取得する場合はStringBuilderが必要になります。
フォームのタイトル文字列"Form1"を取得して ダイアログに表示するプログラムです。
<実行結果>
  
   
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices; // DLL Import


namespace LPTSTR_を使用する場合StringBuilder   //出力パラメータとして LPTSTR を使用する場合、StringBuilder を利用する
{
    public partial class Form1 : Form
    {
        [DllImport("User32.Dll", EntryPoint = "GetWindowText")]
        public static extern int GetWindowText(int hwnd, StringBuilder buf, int nMaxCount);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            int hwnd = (int)this.Handle;
            StringBuilder buf = new StringBuilder(256);
            GetWindowText(hwnd, buf, buf.Capacity); //フォームのタイトルをbufに取得

            MessageBox.Show(buf.ToString());    //メッセージボックスにbufを表示


        }
    }
}



 




<実行前>



<実行後>

■ DLL関数から 参照で整数を取得する

GetNumberOfConsoleMouseButtons( )によりマウスのボタン数を取得するプログラム
 GetNumberOfConsoleMouseButtons( ) の諸元は下記です。(MSDNより)
 BOOL GetNumberOfConsoleMouseButtons(
 LPDWORD lpNumberOfMouseButtons // ボタン数のアドレス
 );
 パラメータ
 lpNumberOfMouseButtons: マウスのボタン数を受け取る 32 ビット変数へのポインタを指定します。

 戻り値
 関数が成功すると、0 以外の値が返ります。
 関数が失敗すると、0 が返ります。
<実行結果>

          

   
//Program.cs
//GetNumberOfConsoleMouseButtons( )によりマウスのボタン数を取得するプログラム

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Runtime.InteropServices; // 追加

namespace DDL_Get_NumMouseBtn
{
    class Program
    {
        //宣言部
        [DllImport("kernel32.dll")]
        extern static bool GetNumberOfConsoleMouseButtons(ref uint lpNumberOfMouseButtons);
       
        static void Main(string[] args)
        {
            uint Num;
            bool Flag = false;

            Num = 8;

            Console.Write("DLL呼出し前: Num = {0:d}\n", Num);
            Console.Write("DLL呼出し前: Flag = {0:d}\n", Flag);


            Flag = GetNumberOfConsoleMouseButtons(ref Num); //関数の呼び出し

          
            Console.Write("DLL呼出し後: Num = {0:d}\n", Num);
            Console.Write("DLL呼出し後: Flag = {0:d}\n", Flag);




        }
    }
}


<上記コンソール内の文字のをコピー>
DLL呼出し前: Num = 8
DLL呼出し前: Flag = False
DLL呼出し後: Num = 5
DLL呼出し後: Flag = True
続行するには何かキーを押してください . . .



■ 定数を使用するDLL関数の呼び出し

定数を使用するDLL関数を呼び出す場合、MSDNで定数を調べて呼び出す前に定義しておく必要があります。 <実行結果>

          

   <プログラム例>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Runtime.InteropServices;

namespace 定数を使用するDLL関数

{
    class Program
    {
        [DllImport("kernel32.dll")]
        private extern static bool IsProcessorFeaturePresent
          (uint ProcessorFeature);


        // 定数の宣言
        private const uint PF_XMMI_INSTRUCTIONS_AVAILABLE = 6;

        static void Main(string[] args)
        {
            if (IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE))
            {
                Console.WriteLine("SSE is available.");
            }
            else
            {
                Console.WriteLine("SSE is not available.");
            }

        }
    }
}

 



<上記コンソールの文字のコピー>
SSE is available.
続行するには何かキーを押してください .

. .

■ DLL関数に構造体を渡す

REC構造体をC#側で再定義して DLL関数の構造体(参照)を呼び出します <実行結果>

    

          

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices; // DLL Import


namespace Netx3_Class_
{
    public partial class Form1 : Form
    {

        public struct RECT      //C#側の構造体を定義する
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }
         
        [DllImport("User32.Dll", EntryPoint = "GetWindowRect")]
        public static extern int GetWindowRect(int hwnd, ref RECT rc);  //構造体:参照
     

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            RECT rc = new RECT();
            int hwnd = (int)this.Handle;
            GetWindowRect(hwnd, ref rc);    //構造体を参照渡し

            string msg = String.Format("top={0}, left={1}, right={2}, bottom={3}."
                                        , rc.top, rc.left, rc.right, rc.bottom);
            MessageBox.Show(msg);


        }
    }
}


 


 DLL関数(ファイル)をC#でつくる方法 


1. DLL作成用の英数字名のフォルダ DLL_makeを作成する。

2. 以下の Add.cs ファイル(テキストファイル)をつくる。

3. 以下の Mult.csファイル(テキストファイル)をつくる。

4. コンソールウィンドウを開く

5. エクスプローラを開きDLL_makeフォルダを表示する

6. コンソールウィンドウで ディレクトリ変更コマンドcdをキーインする。

7. エクスプローラのDLL_makeフォルダをcd の引数部にコピーする。

8. 不要な" "を削除後に、cdを実行して カレントディレクトリを変更する。

9. DLL_makeフォルダに csc.exeがあるC:\Windows\Microsoft.NET\Framework\v3.5のパスをとおす。

10. DLL_makeフォルダの中で、コンパイラオプションtarget:library(DLL作成オプション)をつけた以下のコマンドを実行する。
 csc /target:library /out:MathLibrary.DLL Add.cs Mult.cs
  out:の後が 作成されるDLLのファイル名となる。 そのあとにコンパイルされるAdd.cs Mult.csを引数として追加する。

11.実行後正常であれば 以下の表示がコマンドラインに表示され MathLibrary.DLL のDLLファイルが作成される。

Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.6400
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

12.次のコマンド ラインを使用して、実行ファイルTestCode.exeを作成する。

csc /out:TestCode.exe /reference:MathLibrary.DLL TestCode.cs 

13. プログラム(実行ファイル)estCode.exeを実行するには、EXE ファイルの名前に続けて 2 つの数値を入力する。
TestCode 1234 5678
14. 実行結果は以下のようにコマンドラインに表示される。

C:\Users\ysaito\Documents\Visual Studio 2012\Projects\C#_note\C#2012_note\Test\D
LL_make>TestCode 1234 5678
Calling methods from MathLibrary.DLL:
1234 + 5678 = 6912
1234 * 5678 = 7006652
<実行結果>

          

  
// File: Add.cs 
namespace UtilityMethods
{
    public class AddClass 
    {
        public static long Add(long i, long j) 
        { 
            return (i + j);
        }
    }
}
//--------------------------------------------

// File: Mult.cs
namespace UtilityMethods 
{
    public class MultiplyClass
    {
        public static long Multiply(long x, long y) 
        {
            return (x * y); 
        }
    }
}

//--------------------------------------------------
// File: TestCode.cs

using UtilityMethods;

class TestCode
{
    static void Main(string[] args) 
    {
        System.Console.WriteLine("Calling methods from MathLibrary.DLL:");

        if (args.Length != 2)
        {
            System.Console.WriteLine("Usage: TestCode <num1> <num2>");
            return;
        }

        long num1 = long.Parse(args[0]);
        long num2 = long.Parse(args[1]);

        long sum = AddClass.Add(num1, num2);
        long product = MultiplyClass.Multiply(num1, num2);

        System.Console.WriteLine("{0} + {1} = {2}", num1, num2, sum);
        System.Console.WriteLine("{0} * {1} = {2}", num1, num2, product);
    }
}
/* Output (assuming 1234 and 5678 are entered as command line args):
    Calling methods from MathLibrary.DLL:
    1234 + 5678 = 6912
    1234 * 5678 = 7006652        
*/